using System;
using System.Windows;
using System.Windows.Input;
using Microsoft.Phone.Shell;

namespace Microsoft.Phone.Controls
{
    /// <summary>
    /// Bindable <see cref="IApplicationBarMenuItem">IApplicationBarMenuItem</see> alternative base implementation.
    /// </summary>
    public abstract class ApplicationBarItemBase : FrameworkElement, IApplicationBarMenuItem
    {
        /// <summary>
        /// Occurs when the user taps the menu item.
        /// </summary>
        public event EventHandler Click;

        #region Dependency Properties

        /// <summary>
        /// <see cref="DependencyProperty">Dependency Property</see>.
        /// </summary>
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.RegisterAttached("Command",
                                                typeof (ICommand),
                                                typeof(ApplicationBarItemBase),
                                                null);

        /// <summary>
        /// <see cref="DependencyProperty">Dependency Property</see>.
        /// </summary>
        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.RegisterAttached("CommandParameter",
                                                typeof (object),
                                                typeof(ApplicationBarItemBase),
                                                null);

        /// <summary>
        /// <see cref="DependencyProperty">Dependency Property</see>.
        /// </summary>
        public static readonly DependencyProperty CommandParameterValueProperty =
            DependencyProperty.RegisterAttached("CommandParameterValue",
                                                typeof (object),
                                                typeof(ApplicationBarItemBase),
                                                null);

        /// <summary>
        /// <see cref="DependencyProperty">Dependency Property</see>.
        /// </summary>
        public static readonly DependencyProperty ShouldFocusPageOnClickProperty =
            DependencyProperty.RegisterAttached("ShouldFocusPageOnClick",
                                                typeof(bool),
                                                typeof(ApplicationBarItemBase),
                                                new PropertyMetadata(false));

        /// <summary>
        /// <see cref="DependencyProperty">Dependency Property</see>.
        /// </summary>
        public static readonly DependencyProperty IsEnabledProperty =
            DependencyProperty.RegisterAttached("IsEnabled",
                                                typeof (bool),
                                                typeof (ApplicationBarItemBase),
                                                new PropertyMetadata(true, OnIsEnabledChanged));

        /// <summary>
        /// <see cref="DependencyProperty">Dependency Property</see>.
        /// </summary>
        public static readonly DependencyProperty TextProperty =
            DependencyProperty.RegisterAttached("Text",
                                                typeof (string),
                                                typeof (ApplicationBarItemBase),
                                                new PropertyMetadata(OnTextChanged));

        #endregion

        #region Bindable Properties

        /// <summary>
        /// Gets or sets <see cref="ICommand">command</see>.
        /// </summary>
        public ICommand Command
        {
            get { return (ICommand) GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        /// <summary>
        /// Gets or sets command parameter.
        /// </summary>
        public object CommandParameter
        {
            get { return GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }

        /// <summary>
        /// Gets or sets command parameter value.
        /// </summary>
        public object CommandParameterValue
        {
            get { return GetValue(CommandParameterValueProperty); }
            set { SetValue(CommandParameterValueProperty, value); }
        }

        /// <summary>
        /// Gets or sets flag that triggers focus on page after click.
        /// </summary>
        public bool ShouldFocusPageOnClick
        {
            get { return (bool)GetValue(ShouldFocusPageOnClickProperty); }
            set { SetValue(ShouldFocusPageOnClickProperty, value); }
        }

        /// <summary>
        /// Gets or sets the enabled status of the menu item.
        /// </summary>
        /// <returns>
        /// true if the menu item is enabled; otherwise, false.
        /// </returns>
        public bool IsEnabled
        {
            get { return (bool) GetValue(IsEnabledProperty); }
            set { SetValue(IsEnabledProperty, value); }
        }

        /// <summary>
        /// The string to display on the menu item.
        /// </summary>
        /// <returns>
        /// Type: <see cref="T:System.String"/>.
        /// </returns>
        public string Text
        {
            get { return (string) GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        #endregion

        /// <summary>
        /// Handles click event.
        /// </summary>
        /// <param name="sender">Target.</param>
        /// <param name="e">Arguments.</param>
        protected virtual void OnClick(object sender, EventArgs e)
        {
            if (ShouldFocusPageOnClick)
            {
                var page = this.FindVisualParent<PhoneApplicationPage>();

                if (page != null)
                {
                    page.Focus();
                }
            }

            if (Command != null)
            {
                Command.Execute(CommandParameter ?? CommandParameterValue);
            }

            Click.Fire(sender, e);
        }

        /// <summary>
        /// When implemented processes Text property changes.
        /// </summary>
        /// <param name="text"></param>
        protected abstract void OnTextChanged(string text);

        /// <summary>
        /// When implemented processes IsEnabled property changes.
        /// </summary>
        /// <param name="isEnabled"></param>
        protected abstract void OnIsEnabledChanged(bool isEnabled);

        private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is ApplicationBarItemBase)) return;

            if (e.NewValue == e.OldValue) return;

            (d as ApplicationBarItemBase).OnIsEnabledChanged((bool)e.NewValue);
        }

        private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is ApplicationBarItemBase)) return;

            if (e.NewValue == e.OldValue) return;

            (d as ApplicationBarItemBase).OnTextChanged(e.NewValue as string);
        }
    }
}